/**
 * Copyright 2008 Adam Ruggles.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.google.code.sapien.guice.jpa;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.code.sapien.util.ContextManager;
import com.google.inject.Inject;

/**
 * Entity Manager Proxy.
 * @author Adam
 * @version $Id: EntityManagerProxy.java 25 2009-05-26 04:23:01Z a.ruggles $
 * 
 * Created on Oct 8, 2008 at 9:28:30 PM 
 */
public class EntityManagerProxy implements InvocationHandler {
	/**
     * The <code>Logger</code> is used by the application to generate a log messages.
     */
    private static final Logger LOG = LoggerFactory.getLogger(EntityManagerProxy.class);

    /**
     * The Entity Manager Factory.
     */
    private final EntityManagerFactory entityManagerFactory;

    /**
     * Constructs a Entity Manager Proxy.
     * @param entityManagerFactory The Entity Manager Factory.
     */
    @Inject
    public EntityManagerProxy(final EntityManagerFactory entityManagerFactory) {
    	super();
    	this.entityManagerFactory = entityManagerFactory;
    }

	/**
	 * Closes the entity manager.
	 */
	private void closeEntityManager() {
		if (ContextManager.isBound(EntityManager.class)) {
			final EntityManager entityManager = ContextManager.unbind(EntityManager.class);
			if (entityManager != null && entityManager.isOpen()) {
				LOG.debug("Closing Entity Manager {}", entityManager);
				entityManager.close();
			}
		}
	}

	/**
	 * {@inheritDoc}
	 * @see java.lang.reflect.InvocationHandler#invoke(java.lang.Object, java.lang.reflect.Method, java.lang.Object[])
	 */
	public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable {
		Object result = null;
		try {
			if ("close".equals(method.getName())) {
				closeEntityManager();
			} else {
				EntityManager entityManager = ContextManager.getBind(EntityManager.class);
				if (entityManager == null) {
					entityManager = entityManagerFactory.createEntityManager();
					LOG.debug("Binding new Entity Manager [{}]", entityManager);
					ContextManager.bind(EntityManager.class, entityManager);
				}
				LOG.debug("invoking [{}] on the EntityManager [{}]", method.getName(), entityManager);
				result = method.invoke(entityManager, args);
			}
		} catch (InvocationTargetException e) {
			throw e.getTargetException();
		}
		return result;
	}
}
